<script lang="ts" setup>
import { showFailToast, showSuccessToast } from 'vant'
import { ContactApi } from '~/api/ContactApi'
import LxButton from '~/components/common/LxButton.vue'
import { ContactModel } from '~/model'

const router = useRouter()

const fileInput = ref(null as any)
const serverContacts = ref([] as ContactModel[])
const resolvedContacts = ref([] as ContactModel[])

// new contact checked
const checked = ref([] as boolean[])

const loaded = computed(() => !!resolvedContacts.value.length)

const existedContacts = computed(() => {
  const phoneArr = serverContacts.value.map(x => x.phone)
  const mapPhoneOldName = new Map(serverContacts.value.map(x => [x.phone, x.name]))

  return resolvedContacts.value.filter((x) => {
    const exist = phoneArr.includes(x.phone)
    const hasNewName = mapPhoneOldName.get(x.phone) !== x.name
    return exist && hasNewName
  }).map(x => ({
    ...x,
    oldName: mapPhoneOldName.get(x.phone),
  }))
})

const newContacts = computed(() => {
  const phoneArr = serverContacts.value.map(x => x.phone)
  return resolvedContacts.value.filter(x => !phoneArr.includes(x.phone))
})

onMounted(() => {
  ContactApi.getAll()
    .then((res) => {
      serverContacts.value = res
    })
})

function decodeQuotedPrintable(raw: string, charset = 'utf-8') {
  // console.log(raw)

  const dc = new TextDecoder(charset)
  return raw.replace(/[\t\x20]$/gm, '').replace(/=(?:\r\n?|\n)/g, '').replace(/((?:=[a-fA-F0-9]{2})+)/g, (m) => {
    const cd = m.substring(1).split('=')
    const uArr = new Uint8Array(cd.length)
    for (let i = 0; i < cd.length; i++) {
      uArr[i] = Number.parseInt(cd[i], 16)
    }
    const ret = dc.decode(uArr)
    // console.log(ret)

    return ret
  })
}

function resolveVcf(text: string): ContactModel[] {
  const DEVIDER = '########'
  const _text = text.replaceAll('\r\n', DEVIDER).replaceAll(/=\s=/g, '=')
  const contacts = [] as ContactModel[]

  const matchResult = _text.matchAll(/BEGIN:VCARD(.*?)END:VCARD/g)
  // const stringCards = []
  for (const item of matchResult) {
    const stringCard = item[1]

    const m1 = stringCard.match(/#N;.*QUOTED-PRINTABLE:;([=0-9A-Z#\s]+);;;/)
    if (!m1 || !m1[1]) {
      console.warn('name没有匹配成功', m1)
      console.log(stringCard)

      continue
    }
    const name = decodeQuotedPrintable(m1[1].replaceAll(`=${DEVIDER}=`, '='))

    const m2 = stringCard.match(/#TEL;CELL:([0-9\s]+)#/)
    // TEL;CELL:135 6559 0858
    if (!m2 || !m2[1]) {
      console.warn('phone没有匹配成功', m2)
      console.log(stringCard)

      continue
    }
    const phone = m2[1].replaceAll(' ', '')

    const contact = new ContactModel()
    contact.name = name
    contact.phone = phone
    contacts.push(contact)
  }

  return contacts
}

async function fileUpload() {
  const file: File = fileInput.value.files[0]
  const text = await file.text()

  const ret = resolveVcf(text)
  checked.value = Array(ret.length).fill(false)
  resolvedContacts.value = ret
}

function batchImport() {
  const selected = [] as ContactModel[]
  newContacts.value.forEach((contact, i) => {
    if (checked.value[i]) {
      selected.push(contact)
    }
  })
  if (!selected.length) {
    showFailToast('请选择联系人')
    return
  }
  ContactApi.addBatch(selected)
    .then((res) => {
      if (res) {
        showSuccessToast('导入成功')
        window.setTimeout(() => {
          router.back()
        })
      }
      else {
        showFailToast('出错了，请联系管理员')
      }
    })
}

// const checked = ref(new Map<number, boolean>())
function toggleCheckAll() {
  const allChecked = !checked.value.includes(false)

  // 全选清空，没有
  for (let i = 0; i < checked.value.length; i++) {
    checked.value[i] = !allChecked
  }
}
</script>

<template>
  <div h-full p4>
    <div v-if="!loaded" mb2 h-full flex-cc>
      <input
        id="stub"
        ref="fileInput"
        type="file"
        name="avatar"
        accept=".vcf"
        hidden
        @change="fileUpload"
      >
      <div b="1 gray-5 dashed" h-60vh w-80vw flex-cc @click="fileInput.click">
        上传联系人
      </div>
    </div>
    <div v-else>
      <div mb2>
        已存在（不同名）{{ existedContacts.length }}个
      </div>

      <div mb max-h-240px of-auto>
        <table class="lx-table">
          <thead>
            <tr>
              <td>姓名/单位</td>
              <td>
                电话
              </td>
              <td>已存为</td>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(item, index) in existedContacts" :key="index">
              <td>
                {{ item.name }}
              </td>
              <td font-mono>
                {{ item.phone }}
              </td>
              <td>
                {{ item.oldName }}
              </td>
            </tr>
          </tbody>
        </table>
      </div>

      <div mb2>
        新联系人 {{ newContacts.length }}个
      </div>

      <div mb max-h-240px of-auto>
        <table class="lx-table">
          <thead>
            <tr>
              <td>
                <input type="checkbox" @click="toggleCheckAll">
              </td>
              <td>
                姓名/单位
              </td>
              <td>电话</td>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(item, index) in newContacts" :key="index">
              <td>
                <input v-model="checked[index]" type="checkbox">
              </td>
              <td>
                {{ item.name }}
              </td>
              <td font-mono>
                {{ item.phone }}
              </td>
            </tr>
          </tbody>
        </table>
      </div>

      <LxButton w-full icon="plus" title="导入" :action="batchImport" />
    </div>
  </div>
</template>

<style scoped>
.font-mono {
  font-family: "DM Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
    "Liberation Mono", "Courier New", monospace;
}
</style>

<route lang="yaml">
meta:
  title: 批量导入
</route>
